/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package com.ericsson.nfc.signing.algorithm;

import com.ericsson.nfc.signing.bc.crypto.signers.DssSigValue;
import java.io.IOException;
import java.math.BigInteger;
import org.bouncycastle.asn1.ASN1InputStream;
import org.bouncycastle.asn1.ASN1Sequence;
import org.bouncycastle.asn1.DEREncodable;
import org.bouncycastle.asn1.DERInteger;
import org.bouncycastle.asn1.DERNull;
import org.bouncycastle.asn1.DERObject;
import org.bouncycastle.asn1.DERObjectIdentifier;
import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
import org.bouncycastle.asn1.x509.DSAParameter;
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
import org.bouncycastle.crypto.Digest;
import org.bouncycastle.crypto.params.DSAParameters;
import org.bouncycastle.crypto.params.DSAPublicKeyParameters;
import org.bouncycastle.crypto.signers.DSASigner;

/**
 *
 * @author emarkki
 */
public class DSAVerifier implements SignatureVerifier {

    public static final DERObjectIdentifier id_dsa = new DERObjectIdentifier("1.2.840.10040.4.1");
    public static final DERObjectIdentifier id_dsa_with_sha1 = new DERObjectIdentifier("1.2.840.10040.4.3");
    
    private static DSAVerifier instance;
    
    public static DSAVerifier getInstance() {
        if(instance == null) {
            instance = new DSAVerifier();
        }
        return instance;
    }
    
    public boolean verifySignature(byte[] signature, byte[] coveredBytes, PublicKey key) {

        try {
            BigInteger[] rs = decode(signature);
            BigInteger r = rs[0];
            BigInteger s = rs[1];
            
            SubjectPublicKeyInfo pkInfo = key.getSubjectPublicKeyInfo();
            
            AlgorithmIdentifier algId = pkInfo.getAlgorithmId();
            
            if(!algId.getObjectId().equals(id_dsa) && !algId.getObjectId().equals(id_dsa_with_sha1)) {
                throw new IllegalArgumentException("Not a DSA public key");
            }
            
            DERInteger derY = (DERInteger)pkInfo.getPublicKey();

            // DSA Domain parameters
            DEREncodable de = pkInfo.getAlgorithmId().getParameters();
            DSAParameter param = null;
            
            if (de == null || (de.getDERObject() instanceof DERNull)) {
                if(key.getExternalParameters() instanceof DSAParameter) {
                    param = (DSAParameter) key.getExternalParameters();
                }
            } else {
                param = DSAParameter.getInstance(de.getDERObject());
            }
            if(param == null) {
                return false; // No parameters
            }
            DSAParameters parameters = new DSAParameters(param.getP(), param.getQ(), param.getG());
            
            DSASigner signer = new DSASigner();
            signer.init(false, new DSAPublicKeyParameters(derY.getValue(), parameters));

            Digest digest = DigestFactory.getDigest("SHA-1");
            digest.update(coveredBytes, 0, coveredBytes.length);
            byte[] message = new byte[digest.getDigestSize()];
            digest.doFinal(message, 0);
            
            return signer.verifySignature(message, r, s);

        } catch (IOException ex) {
            ex.printStackTrace();
            throw new RuntimeException("IOException: " + ex.getMessage());
        }
    }
    
    static byte[] encode(BigInteger r, BigInteger s) {
        return new DssSigValue(r, s).getDEREncoded();
    }
    
    static BigInteger[] decode(byte[] signature) throws IOException {
        ASN1InputStream aIn = new ASN1InputStream(signature);
        DERObject o = aIn.readObject();

        ASN1Sequence encodedSeq = (ASN1Sequence) o;
        DssSigValue ecdsaSigValue = new DssSigValue(encodedSeq);
        
        return new BigInteger[]{ecdsaSigValue.getR(), ecdsaSigValue.getS()};
    }

}
